/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2025-01-01 14:52:23
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-04 22:09:50
 * @FilePath: /FileBackup/include/fileCompress.hpp
 * @Description: 文件压缩模块 Huffman 算法
 */
#pragma once

#include "fileBaseUnit.hpp"
#include <queue>
#include <unordered_map>
#include <endian.h>

struct HuffmanTNode
{
    unsigned char token; // ACSⅡ编码进行统计
    std::string code;    // Huffman 编码
    uint64_t frequency;  // 词频（根结点 = 文件大小）
    struct HuffmanTNode *lchild;
    struct HuffmanTNode *rchild;
};

struct HTNodeCompare
{
    bool operator()(const HuffmanTNode *a, const HuffmanTNode *b)
    {
        return (a->frequency > b->frequency);
    }
};

class FileCompressor
{
public:
    std::filesystem::path sourceFilePath; // 原文件路径
    std::filesystem::path targetFilePath; // 目标文件路径

private:
    uint64_t tokenTable[TOKEN_NUM];                           // 词频表
    std::unordered_map<unsigned char, std::string> codeTable; // 编码表
    std::priority_queue<
        HuffmanTNode *, std::vector<HuffmanTNode *>, HTNodeCompare>
        frequencyHeap; // 词频最小堆

    HuffmanTNode *huffmanTree;

    /// @brief 词频统计
    /// @param source_file 原文件数据流
    void CountToken(FileBaseUnit &source_file);

    /// @brief 写入词频表 & 转换为大端字节序
    /// @param target_file 目标文件输出流
    void WriteToken(FileBaseUnit &target_file);

    /// @brief 读取词频表 & 从大端字节序转换回主机字节序
    /// @param source_file 原文件数据流
    void ReadToken(FileBaseUnit &source_file);

    /// @brief 根据词频表构建 Huffman 树
    void CreateHuffmanTree();

    /// @brief 后序遍历删除 Huffman 树
    /// @param root 根节点
    void DeleteHuffmanTree(HuffmanTNode *root);

    /// @brief 遍历 Huffman 树生成每个叶结点编码 左0 右1
    /// @param root 当前根结点
    /// @param code 编码
    void HuffmanEncode(HuffmanTNode *root, std::string code);

    /// @brief 数据编码压缩
    /// @param source_file 原文件数据流
    /// @param target_file 目标文件输出流
    bool EncodeFileData(FileBaseUnit &source_file, FileBaseUnit &target_file);

    /// @brief 数据解码解压
    /// @param source_file 原文件数据流
    /// @param target_file 目标文件输出流
    /// @param file_size 文件原始大小
    bool DecodeFileData(FileBaseUnit &source_file, FileBaseUnit &target_file, off_t file_size);

public:
    /// @brief 文件压缩
    /// @return 压缩成功 `true`
    bool Compress();

    /// @brief 文件解压缩
    /// @param go_back 还原文件原路径
    /// @return 解压成功 `true`
    bool Decompress(bool go_back);

    FileCompressor(std::filesystem::path source_file_path,
                   std::filesystem::path target_file_path);
    ~FileCompressor();
};
